#include <stdio.h>
#include <math.h>
#include <string.h>
#include "slave.h"

__thread_local volatile unsigned long get_reply,put_reply;

__thread_local double ex_slave[TZ1][TZ2];
__thread_local double ey_slave[TZ1][TZ2];
__thread_local double hz_slave[TZ1][TZ2];

__thread_local_fix double shade_hz_left[TZ1];//hz[i][j-1]
__thread_local_fix double shade_hz_up[TZ2];//hz[i-1][j]
__thread_local_fix double shade_ex[TZ1];//ex[i][j+1]
__thread_local_fix double shade_ey[TZ2];//ey[i+1][j]


extern double ex[NX][NY+1];
extern double ey[NX+1][NY];
extern double hz[NX][NY];

extern int thread_nums, iter, N, N_loop1, ntiley,ntilex;
extern int loop1_startj[64],starti[64], startj[64];
#define alpha_d 0.0876
#define beta_d  0.0765

#define min(a,b) ((a>b)?(b):(a))
#define max(a,b) ((a>b)?(a):(b))

void func()
{
   int i,j,k,n,jj,ii,my_id;//N ~ average num tiles that each thread assigned
   int dest,src,my_tiles,loop1_my_tils;
   int cur_blky, cur_blkz;
   int tsize, bsize, stride;
   
   my_id = athread_get_id(-1);
   //dest = iter % 2 ;
   //src = 1-dest;
   
   if(my_id<thread_nums-1)
   {
      my_tiles=N;
      loop1_my_tils=N_loop1;
   }else{
      my_tiles=ntiley*ntilex-N*(thread_nums-1);
      loop1_my_tils=ntiley-N_loop1*(thread_nums-1);
   }
   //loop1
   n = 0;
   for(j = loop1_startj[my_id]; n < loop1_my_tils && j < (NY)-TZ2+1 && my_id<thread_nums; j += TZ2,n++)
   {
     //compute
     for(jj=0;jj<min(NY,TZ2);jj++)
       ey_slave[0][jj]=iter;
     //write back
     put_reply=0;
     athread_put(PE_MODE, &ey_slave[0][0], &ey[0][j], TZ2*8, (void*)&put_reply, 0, 0); 
     while(put_reply!=1);
   }
   //barrier
   athread_syn(ARRAY_SCOPE,-1);
   //loop2
   n=0;
   for(i = starti[my_id];  i < (NX)-TZ1+1 && my_id<thread_nums; i += TZ1)
     for(j = startj[my_id]; n < my_tiles &&j < (NY)-TZ2+1; j += TZ2,n++)
     {
       int slave_i=0;
       int dma_num=0;
       //fetch ey data
       get_reply=0;
       bsize=TZ2*8;
       stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
       tsize= bsize*TZ1;
       athread_get(PE_MODE,&ey[i][j]
              , &ey_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       //fetch hz data and shade data
       bsize=TZ2*8;
       stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(TZ1);
       athread_get(PE_MODE,&hz[i][j]
              , &hz_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       dma_num+=2;
       if(max(i,1)!=1)
       {
         athread_get(PE_MODE,&hz[i-1][j],&shade_hz_up[0],TZ2*8,(void*)&get_reply,0,0,0); 
         dma_num++;
       }
       while(get_reply!=dma_num);
       //compute
       for(ii=1;ii<TZ1;ii++)
         for(jj=0;jj<TZ2;jj++) 
           ey_slave[ii][jj] = ey_slave[ii][jj] - 0.5*(hz_slave[ii][jj] - hz_slave[ii-1][jj]);
       if(max(i,1)!=1)
       {
         for(jj=0;jj<TZ2;++jj)
           ey_slave[0][jj] = ey_slave[0][jj] - 0.5*(hz_slave[0][jj] - shade_hz_up[jj]);
       }
       //write back
       put_reply=0;
       if(i==0)
       {  
         slave_i=1;
       }else{
         slave_i=0;
       }
       bsize=TZ2*8;
       stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead
       tsize= bsize*(TZ1-slave_i);
       athread_put(PE_MODE,&ey_slave[slave_i][0]
              , &ey[max(i,1)][j]
              ,tsize,(void*)&put_reply,stride,bsize);
       while(put_reply!=1);
     }
   //barrier
   athread_syn(ARRAY_SCOPE,-1);
   //loop3
   n=0;
   for(i = starti[my_id];  i < (NX)-TZ1+1 && my_id<thread_nums; i += TZ1)
     for(j = startj[my_id]; n < my_tiles &&j < (NY) -TZ2+1; j += TZ2,n++)
     {
       int dma_put_num = 0;
       int dma_get_num = 0;
       get_reply=0;
       //fetch ex data
       bsize=TZ2*8;
       stride=(NY+1-TZ2)*8;
       tsize= bsize*TZ1;
       athread_get(PE_MODE,&ex[i][j]
              , &ex_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize); 
       //fetch hz data
       bsize=TZ2*8;
       stride=(NY-TZ2)*8; 
       tsize= bsize*TZ1;
       athread_get(PE_MODE,&hz[i][j]
              , &hz_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       dma_get_num+=2;
       //fetch hz shade data
       if(max(j,1)!=1)
       {
         bsize=8;
         stride=(NY-1)*8; 
         tsize= bsize*TZ1;
         athread_get(PE_MODE,&hz[i][j-1]
              , &shade_hz_left[0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
         dma_get_num++;
       }
       while(get_reply!=dma_get_num);
       //compute
       for( ii= 0; ii < TZ1; ii++)
        for(jj = 1; jj < TZ2; jj++)
         ex_slave[ii][jj] = ex_slave[ii][jj] - 0.5*(hz_slave[ii][jj] - hz_slave[ii][jj-1]);
       if(max(j,1)!=1)
        for( ii= 0; ii < TZ1; ii++)
          ex_slave[ii][0] = ex_slave[ii][0] - 0.5*(hz_slave[ii][0] - shade_hz_left[ii]);
       //write back
       put_reply=0;
       if(max(j,1)==1)
       {   
         for(ii=0;ii<TZ1;++ii)
           athread_put(PE_MODE,&ex_slave[ii][1]
              , &ex[i+ii][1]
              ,(TZ2-1)*8,(void*)&put_reply,0,0);  
         dma_put_num+=TZ1;
       }else{
         bsize=(TZ2)*8;
         stride=(NY+1-(TZ2))*8; 
         tsize= bsize*TZ1;
         athread_put(PE_MODE,&ex_slave[0][0]
              , &ex[i][j]
              ,tsize,(void*)&put_reply,stride,bsize); 
         dma_put_num++;
       }
       while(put_reply!=dma_put_num);
     }
   //barrier
   athread_syn(ARRAY_SCOPE,-1);
   //loop4
   n=0;
   for(i = starti[my_id]; i < (NX)-TZ1+1 && my_id<thread_nums; i += TZ1)
     for(j = startj[my_id];  n < my_tiles&&j < (NY)-TZ2+1; j += TZ2,n++)
     {
         
       //printf("my_id=%d\n",my_id);
       //printf("i=%d,j=%d,my_tiles=%d\n",i,j,my_tiles);
       get_reply=0;
       //fetch ex  data 
       bsize=(TZ2)*8;
       stride=(NY+1-TZ2)*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(TZ1);
       athread_get(PE_MODE,&ex[i][j]
              , &ex_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       //fetch ey data
       bsize=TZ2*8;
       stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(TZ1);
       athread_get(PE_MODE,&ey[i][j]
              , &ey_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       //fetch hz data
       bsize=TZ2*8;
       stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(TZ1);
       athread_get(PE_MODE,&hz[i][j]
              , &hz_slave[0][0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       //fetch ex shada data
       bsize=8;
       stride=(NY+1-1)*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(TZ1);
       athread_get(PE_MODE,&ex[i][j+TZ2]
              , &shade_ex[0]
              , tsize, (void*)&get_reply
              , 0, stride, bsize);
       //fetch ey shade data
       athread_get(PE_MODE,&ey[i+TZ1][j],&shade_ey[0],TZ2*8,(void*)&get_reply,0,0,0);
       while(get_reply!=5);
       //compute
       for(ii=0; ii<TZ1-1; ii++)
         for(jj=0; jj<TZ2-1; jj++)
         {
             hz_slave[ii][jj] = hz_slave[ii][jj]-0.7*(ex_slave[ii][jj+1] \
                             - ex_slave[ii][jj] + ey_slave[ii+1][jj] - ey_slave[ii][jj]);
                             //if(i==2&&j==0) printf("asaaaaaaaaaaa\n");
         }
           
       for(jj = 0; jj < TZ2-1; jj++)
       {
           
           //if(i==0&&j==2)
           //printf("%f-0.7*(%f-%f+%f-%f)\n",hz_slave[TZ1-1][jj],ex_slave[TZ1-1][jj+1],ex_slave[TZ1-1][jj],shade_ey[jj],ey_slave[TZ1-1][jj]);
           hz_slave[TZ1-1][jj] = hz_slave[TZ1-1][jj] - 0.7*(ex_slave[TZ1-1][jj+1] \
                                - ex_slave[TZ1-1][jj] + shade_ey[jj] - ey_slave[TZ1-1][jj]); 
           //printf("aaaaaaaaaaaa\n");
       }
          
       for(ii=0; ii<TZ1-1; ii++)
       {
            hz_slave[ii][TZ2-1] = hz_slave[ii][TZ2-1] - 0.7*(shade_ex[ii]\
                               - ex_slave[ii][TZ2-1] + ey_slave[ii+1][TZ2-1] - ey_slave[ii][TZ2-1]);
            //printf("assssss\n");
       }
         
       hz_slave[TZ1-1][TZ2-1] = hz_slave[TZ1-1][TZ2-1] - 0.7*(shade_ex[TZ1-1] \
                               - ex_slave[TZ1-1][TZ2-1]+shade_ey[TZ2-1] - ey_slave[TZ1-1][TZ2-1]);
       // if(i=0&&j=2)
        // {
            
        // }  
       //write back
       put_reply=0;
       bsize=TZ2*8;
       stride=(NY-TZ2)*8;
       tsize= bsize*TZ1;
       athread_put(PE_MODE,&hz_slave[0][0]
              , &hz[i][j]
              ,tsize,(void*)&put_reply,stride,bsize);
       while(put_reply!=1);
       // if(j>=NY-TZ2)
         // {
             // j=-TZ2;
         // }
     }
}
